{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Description:\n",
    "这个Jupyter用Pytorch实现GMF模型， 完成该模型的预训练过程。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 导入包"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-10-21T03:00:56.774466Z",
     "start_time": "2020-10-21T03:00:55.055248Z"
    }
   },
   "outputs": [],
   "source": [
    "import datetime\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "from collections import Counter\n",
    "import heapq\n",
    "\n",
    "import torch\n",
    "from torch.utils.data import DataLoader, Dataset, TensorDataset\n",
    "\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "import torch.optim as optim\n",
    "\n",
    "from torchkeras import summary, Model\n",
    "\n",
    "import warnings\n",
    "warnings.filterwarnings('ignore')\n",
    "\n",
    "device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-10-21T03:00:57.500906Z",
     "start_time": "2020-10-21T03:00:57.490934Z"
    }
   },
   "outputs": [],
   "source": [
    "# 一些超参数设置\n",
    "topK = 10\n",
    "num_factors = 8\n",
    "num_negatives = 4\n",
    "batch_size = 64\n",
    "lr = 0.001"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 导入数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-10-21T03:01:01.125762Z",
     "start_time": "2020-10-21T03:00:59.257462Z"
    }
   },
   "outputs": [],
   "source": [
    "# 数据在processed Data里面\n",
    "train = np.load('ProcessedData/train.npy', allow_pickle=True).tolist()\n",
    "testRatings = np.load('ProcessedData/testRatings.npy').tolist()\n",
    "testNegatives = np.load('ProcessedData/testNegatives.npy').tolist()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-10-21T03:01:03.246658Z",
     "start_time": "2020-10-21T03:01:03.233692Z"
    }
   },
   "outputs": [],
   "source": [
    "num_users, num_items = train.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-10-21T03:01:05.619922Z",
     "start_time": "2020-10-21T03:01:05.606957Z"
    }
   },
   "outputs": [],
   "source": [
    "# 制作数据   用户打过分的为正样本， 用户没打分的为负样本， 负样本这里采用的采样的方式\n",
    "def get_train_instances(train, num_negatives):\n",
    "    user_input, item_input, labels = [], [], []\n",
    "    num_items = train.shape[1]\n",
    "    for (u, i) in train.keys():  # train.keys()是打分的用户和商品       \n",
    "        # positive instance\n",
    "        user_input.append(u)\n",
    "        item_input.append(i)\n",
    "        labels.append(1)\n",
    "        \n",
    "        # negative instance\n",
    "        for t in range(num_negatives):\n",
    "            j = np.random.randint(num_items)\n",
    "            while (u, j) in train:\n",
    "                j = np.random.randint(num_items)\n",
    "            #print(u, j)\n",
    "            user_input.append(u)\n",
    "            item_input.append(j)\n",
    "            labels.append(0)\n",
    "    return user_input, item_input, labels"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-10-21T03:01:36.161514Z",
     "start_time": "2020-10-21T03:01:27.195426Z"
    }
   },
   "outputs": [],
   "source": [
    "user_input, item_input, labels = get_train_instances(train, num_negatives)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-10-21T03:01:47.701029Z",
     "start_time": "2020-10-21T03:01:46.769949Z"
    }
   },
   "outputs": [],
   "source": [
    "train_x = np.vstack([user_input, item_input]).T\n",
    "labels = np.array(labels)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-10-21T03:01:55.976612Z",
     "start_time": "2020-10-21T03:01:55.915776Z"
    }
   },
   "outputs": [],
   "source": [
    "# 构建成Dataset和DataLoader\n",
    "train_dataset = TensorDataset(torch.tensor(train_x), torch.tensor(labels).float())\n",
    "dl_train = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-10-21T03:02:04.699632Z",
     "start_time": "2020-10-21T03:02:04.188999Z"
    },
    "collapsed": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[4097, 2904],\n",
      "        [3362,  231],\n",
      "        [ 972, 2974],\n",
      "        [5629,  918],\n",
      "        [4984, 2451],\n",
      "        [2245,   68],\n",
      "        [1998, 1158],\n",
      "        [ 523, 1822],\n",
      "        [2152,  608],\n",
      "        [4288, 2336],\n",
      "        [ 968, 1361],\n",
      "        [ 172,  450],\n",
      "        [ 330,  260],\n",
      "        [1497, 1979],\n",
      "        [1898, 1196],\n",
      "        [5112, 1572],\n",
      "        [4591, 1127],\n",
      "        [ 942,  322],\n",
      "        [ 654,  739],\n",
      "        [1674, 2969],\n",
      "        [3575, 3674],\n",
      "        [1657, 1120],\n",
      "        [3128, 1763],\n",
      "        [3474, 2556],\n",
      "        [4541,  892],\n",
      "        [1169, 1202],\n",
      "        [2327,  759],\n",
      "        [3791, 3007],\n",
      "        [1403,  247],\n",
      "        [5762, 2460],\n",
      "        [ 240, 2783],\n",
      "        [ 147, 1729],\n",
      "        [4727,  999],\n",
      "        [5888, 1528],\n",
      "        [4573,  289],\n",
      "        [4542, 3240],\n",
      "        [1193,  889],\n",
      "        [ 570,  529],\n",
      "        [5781,  273],\n",
      "        [ 509, 2861],\n",
      "        [3194, 2440],\n",
      "        [5557, 1594],\n",
      "        [5025,  390],\n",
      "        [3532,  243],\n",
      "        [ 527, 3540],\n",
      "        [  91,   53],\n",
      "        [  25,   73],\n",
      "        [5081,  174],\n",
      "        [ 811,  223],\n",
      "        [3768, 2367],\n",
      "        [3474, 1765],\n",
      "        [6039, 1058],\n",
      "        [2495, 1145],\n",
      "        [4746, 3556],\n",
      "        [5620,  472],\n",
      "        [ 930,  536],\n",
      "        [4681, 1781],\n",
      "        [4309,  292],\n",
      "        [ 145, 2234],\n",
      "        [2179,  670],\n",
      "        [1732, 3381],\n",
      "        [5442,  972],\n",
      "        [1482, 3593],\n",
      "        [3690, 3600]], dtype=torch.int32) tensor([0., 0., 0., 1., 0., 1., 1., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n",
      "        1., 0., 0., 1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n",
      "        0., 1., 1., 0., 0., 1., 1., 1., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0.,\n",
      "        0., 0., 0., 1., 0., 1., 0., 0., 0., 0.])\n"
     ]
    }
   ],
   "source": [
    "# 测试一下\n",
    "for (x, y) in iter(dl_train):\n",
    "    print(x, y)\n",
    "    break"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## GMF模型\n",
    "这里建立GMF模型， 这个模型的输入就是用户和物品的ID， 然后通过Embedding层得到它的向量， 然后就可以加权(过一个全连接层)得到最后的输出。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-10-21T03:02:13.158823Z",
     "start_time": "2020-10-21T03:02:13.145850Z"
    }
   },
   "outputs": [],
   "source": [
    "class GMF(nn.Module):\n",
    "    \n",
    "    def __init__(self, num_users, num_items, latent_dim, regs=[0, 0]):\n",
    "        super(GMF, self).__init__()\n",
    "        self.MF_Embedding_User = nn.Embedding(num_embeddings=num_users, embedding_dim=latent_dim)\n",
    "        self.MF_Embedding_Item = nn.Embedding(num_embeddings=num_items, embedding_dim=latent_dim)\n",
    "        self.linear = nn.Linear(latent_dim, 1)\n",
    "        self.sigmoid = nn.Sigmoid()\n",
    "    \n",
    "    def forward(self, inputs):\n",
    "        # 这个inputs是一个批次的数据， 所以后面的操作切记写成inputs[0], [1]这种， 这是针对某个样本了， 我们都是对列进行的操作\n",
    "        \n",
    "        # 先把输入转成long类型\n",
    "        inputs = inputs.long()\n",
    "        \n",
    "        # 用户和物品的embedding\n",
    "        MF_Embedding_User = self.MF_Embedding_User(inputs[:, 0])  # 这里踩了个坑， 千万不要写成[0]， 我们这里是第一列\n",
    "        MF_Embedding_Item = self.MF_Embedding_Item(inputs[:, 1])\n",
    "        \n",
    "        # 两个隐向量点积\n",
    "        predict_vec = torch.mul(MF_Embedding_User, MF_Embedding_Item)\n",
    "        \n",
    "        # liner\n",
    "        linear = self.linear(predict_vec)\n",
    "        output = self.sigmoid(linear)\n",
    "        \n",
    "        return output"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-10-21T03:02:21.362053Z",
     "start_time": "2020-10-21T03:02:21.349136Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "----------------------------------------------------------------\n",
      "        Layer (type)               Output Shape         Param #\n",
      "================================================================\n",
      "         Embedding-1                   [-1, 10]              10\n",
      "         Embedding-2                   [-1, 10]              10\n",
      "            Linear-3                    [-1, 1]              11\n",
      "           Sigmoid-4                    [-1, 1]               0\n",
      "================================================================\n",
      "Total params: 31\n",
      "Trainable params: 31\n",
      "Non-trainable params: 0\n",
      "----------------------------------------------------------------\n",
      "Input size (MB): 0.000008\n",
      "Forward/backward pass size (MB): 0.000168\n",
      "Params size (MB): 0.000118\n",
      "Estimated Total Size (MB): 0.000294\n",
      "----------------------------------------------------------------\n"
     ]
    }
   ],
   "source": [
    "# 看一下这个网络\n",
    "model = GMF(1, 1, 10)\n",
    "summary(model, input_shape=(2,))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 建立模型 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-10-21T03:02:31.926333Z",
     "start_time": "2020-10-21T03:02:29.713355Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "GMF(\n",
       "  (MF_Embedding_User): Embedding(6040, 8)\n",
       "  (MF_Embedding_Item): Embedding(3706, 8)\n",
       "  (linear): Linear(in_features=8, out_features=1, bias=True)\n",
       "  (sigmoid): Sigmoid()\n",
       ")"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "## 设置\n",
    "model = GMF(num_users, num_items, num_factors)\n",
    "model.to(device)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-10-21T03:02:40.193102Z",
     "start_time": "2020-10-21T03:02:40.182133Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3706"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "num_items"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-10-21T03:02:51.448530Z",
     "start_time": "2020-10-21T03:02:50.621561Z"
    },
    "collapsed": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[0.4733],\n",
      "        [0.2390],\n",
      "        [0.4200],\n",
      "        [0.5255],\n",
      "        [0.5320],\n",
      "        [0.2139],\n",
      "        [0.5916],\n",
      "        [0.4612],\n",
      "        [0.3235],\n",
      "        [0.4479],\n",
      "        [0.3781],\n",
      "        [0.3905],\n",
      "        [0.5042],\n",
      "        [0.3872],\n",
      "        [0.5845],\n",
      "        [0.6187],\n",
      "        [0.6632],\n",
      "        [0.3778],\n",
      "        [0.6404],\n",
      "        [0.3791],\n",
      "        [0.5026],\n",
      "        [0.5734],\n",
      "        [0.5033],\n",
      "        [0.5278],\n",
      "        [0.5527],\n",
      "        [0.3566],\n",
      "        [0.2545],\n",
      "        [0.4395],\n",
      "        [0.6099],\n",
      "        [0.3664],\n",
      "        [0.5940],\n",
      "        [0.4097],\n",
      "        [0.6597],\n",
      "        [0.2811],\n",
      "        [0.4454],\n",
      "        [0.3674],\n",
      "        [0.4472],\n",
      "        [0.4751],\n",
      "        [0.3975],\n",
      "        [0.5786],\n",
      "        [0.5477],\n",
      "        [0.2984],\n",
      "        [0.5326],\n",
      "        [0.4971],\n",
      "        [0.7942],\n",
      "        [0.4299],\n",
      "        [0.3187],\n",
      "        [0.4110],\n",
      "        [0.4651],\n",
      "        [0.3364],\n",
      "        [0.5610],\n",
      "        [0.3034],\n",
      "        [0.3721],\n",
      "        [0.4213],\n",
      "        [0.3838],\n",
      "        [0.2702],\n",
      "        [0.5062],\n",
      "        [0.4519],\n",
      "        [0.2904],\n",
      "        [0.4895],\n",
      "        [0.3282],\n",
      "        [0.3819],\n",
      "        [0.5377],\n",
      "        [0.4522]], device='cuda:0', grad_fn=<SigmoidBackward>)\n"
     ]
    }
   ],
   "source": [
    "# 简单测试一下模型\n",
    "for (x, y) in iter(dl_train):\n",
    "    x = x.cuda()\n",
    "    print(model(x))\n",
    "    break"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 模型的训练与评估"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 模型评估函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-10-21T03:03:17.439494Z",
     "start_time": "2020-10-21T03:03:17.421542Z"
    }
   },
   "outputs": [],
   "source": [
    "# Global variables that are shared across processes\n",
    "_model = None\n",
    "_testRatings = None\n",
    "_testNegatives = None\n",
    "_K = None\n",
    "\n",
    "# HitRation\n",
    "def getHitRatio(ranklist, gtItem):\n",
    "    for item in ranklist:\n",
    "        if item == gtItem:\n",
    "            return 1\n",
    "    return 0\n",
    "\n",
    "# NDCG\n",
    "def getNDCG(ranklist, gtItem):\n",
    "    for i in range(len(ranklist)):\n",
    "        item = ranklist[i]\n",
    "        if item == gtItem:\n",
    "            return np.log(2) / np.log(i+2)\n",
    "    return 0\n",
    "\n",
    "def eval_one_rating(idx):   # 一次评分预测\n",
    "    rating = _testRatings[idx]\n",
    "    items = _testNegatives[idx]\n",
    "    u = rating[0]\n",
    "    gtItem = rating[1]\n",
    "    items.append(gtItem)\n",
    "    \n",
    "    # Get prediction scores\n",
    "    map_item_score = {}\n",
    "    users = np.full(len(items), u, dtype='int32')\n",
    "    \n",
    "    test_data = torch.tensor(np.vstack([users, np.array(items)]).T).to(device)\n",
    "    predictions = _model(test_data)\n",
    "    for i in range(len(items)):\n",
    "        item = items[i]\n",
    "        map_item_score[item] = predictions[i].data.cpu().numpy()[0]\n",
    "    items.pop()\n",
    "    \n",
    "    # Evaluate top rank list\n",
    "    ranklist = heapq.nlargest(_K, map_item_score, key=lambda k: map_item_score[k])  # heapq是堆排序算法， 取前K个\n",
    "    hr = getHitRatio(ranklist, gtItem)\n",
    "    ndcg = getNDCG(ranklist, gtItem)\n",
    "    return hr, ndcg\n",
    "\n",
    "def evaluate_model(model, testRatings, testNegatives, K):\n",
    "    \"\"\"\n",
    "    Evaluate the performance (Hit_Ratio, NDCG) of top-K recommendation\n",
    "    Return: score of each test rating.\n",
    "    \"\"\"\n",
    "    global _model\n",
    "    global _testRatings\n",
    "    global _testNegatives\n",
    "    global _K\n",
    "    \n",
    "    _model = model\n",
    "    _testNegatives = testNegatives\n",
    "    _testRatings = testRatings\n",
    "    _K = K\n",
    "    \n",
    "    hits, ndcgs = [], []\n",
    "    for idx in range(len(_testRatings)):\n",
    "        (hr, ndcg) = eval_one_rating(idx)\n",
    "        hits.append(hr)\n",
    "        ndcgs.append(ndcg)\n",
    "    return hits, ndcgs   "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-10-21T00:49:17.101447Z",
     "start_time": "2020-10-21T00:49:17.083494Z"
    }
   },
   "source": [
    "### 模型的训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-10-21T03:03:25.841543Z",
     "start_time": "2020-10-21T03:03:25.827579Z"
    }
   },
   "outputs": [],
   "source": [
    "# 训练参数设置\n",
    "loss_func = nn.BCELoss()\n",
    "optimizer = torch.optim.Adam(params=model.parameters(), lr=lr)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-10-21T03:04:34.215953Z",
     "start_time": "2020-10-21T03:03:34.130934Z"
    }
   },
   "outputs": [],
   "source": [
    "# 计算出初始的评估\n",
    "(hits, ndcgs) = evaluate_model(model, testRatings, testNegatives, topK)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-10-21T03:04:42.785379Z",
     "start_time": "2020-10-21T03:04:42.773369Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Init: HR=0.0982, NDCG=0.0427\n"
     ]
    }
   ],
   "source": [
    "hr, ndcg = np.array(hits).mean(), np.array(ndcgs).mean()\n",
    "print('Init: HR=%.4f, NDCG=%.4f' %(hr, ndcg))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2020-10-21T03:09:39.865Z"
    },
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[step = 2000] loss: 0.499\n",
      "[step = 4000] loss: 0.500\n",
      "[step = 6000] loss: 0.500\n",
      "[step = 8000] loss: 0.500\n",
      "[step = 10000] loss: 0.500\n",
      "[step = 12000] loss: 0.501\n",
      "[step = 14000] loss: 0.500\n",
      "[step = 16000] loss: 0.501\n",
      "[step = 18000] loss: 0.501\n",
      "[step = 20000] loss: 0.501\n",
      "[step = 22000] loss: 0.501\n",
      "[step = 24000] loss: 0.501\n",
      "[step = 26000] loss: 0.501\n",
      "[step = 28000] loss: 0.500\n",
      "[step = 30000] loss: 0.500\n",
      "[step = 32000] loss: 0.501\n",
      "[step = 34000] loss: 0.501\n",
      "[step = 36000] loss: 0.501\n",
      "[step = 38000] loss: 0.501\n",
      "[step = 40000] loss: 0.501\n",
      "[step = 42000] loss: 0.501\n",
      "[step = 44000] loss: 0.500\n",
      "[step = 46000] loss: 0.500\n",
      "[step = 48000] loss: 0.500\n"
     ]
    }
   ],
   "source": [
    "# 模型训练 \n",
    "best_hr, best_ndcg, best_iter = hr, ndcg, -1\n",
    "\n",
    "epochs = 20\n",
    "log_step_freq = 2000\n",
    "\n",
    "for epoch in range(epochs):\n",
    "    \n",
    "    # 训练阶段\n",
    "    model.train()\n",
    "    loss_sum = 0.0\n",
    "    for step, (features, labels) in enumerate(dl_train, 1):\n",
    "        \n",
    "        features, labels = features.cuda(), labels.cuda()\n",
    "        # 梯度清零\n",
    "        optimizer.zero_grad()\n",
    "        \n",
    "        # 正向传播\n",
    "        predictions = model(features)\n",
    "        loss = loss_func(predictions, labels)\n",
    "        \n",
    "        # 反向传播求梯度\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "        \n",
    "        # 打印batch级别日志\n",
    "        loss_sum += loss.item()\n",
    "        if step % log_step_freq == 0:\n",
    "            print((\"[step = %d] loss: %.3f\") %\n",
    "                  (step, loss_sum/step))\n",
    "    \n",
    "    # 验证阶段\n",
    "    net.eval()\n",
    "    (hits, ndcgs) = evaluate_model(model, testRatings, testNegatives, configs.topK)\n",
    "    hr, ndcg = np.array(hits).mean(), np.array(ndcgs).mean()\n",
    "    if hr > best_hr:\n",
    "        best_hr, best_ndcg, best_iter = hr, ndcg, epoch\n",
    "        torch.save(model.state_dict(), 'Pre_train/m1-1m_GMF.pkl')  \n",
    "        \n",
    "    info = (epoch, loss_sum/step, hr, ndcg)\n",
    "    print((\"\\nEPOCH = %d, loss = %.3f, hr = %.3f, ndcg = %.3f\") %info)\n",
    "print('Finished Training...') "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
